export default function createSagaMiddleware() {
    function sagaMiddleware({dispatch, action}) {
        function createChannel() {
            let observer = {}

            function subscribe(actionType, callback) {
                observer[actionType] = callback
            }

            function publish(action) {
                if (observer[action.type]) {
                    let next = observer[action.type];
                    delete observer[action.type];  //只执行一次，执行完成后取消订阅
                    next(action);    //继续调用next方法，继续往下执行
                }
            }

            return {subscribe, publish}
        }

        let channel = createChannel();

        function run(generator, callback) {
            let it = typeof generator[Symbol.iterator] === 'function' ? generator : generator();

            function next(nextValue) {
                let {value: effects, done} = it.next(nextValue);
                if (!done) {
                    if (typeof effects[Symbol.iterator] === 'function') {
                        run(effects);   //如果是一个迭代器的话，可以直接传入run方法执行
                        next();
                    } else if (typeof effects.then === 'function') {    //直接返回一个promise
                        effects.then(next,)
                    }
                    {
                        switch (effects.type) {
                            case 'TAKE':   //take的意思是监听某个动作，当动作发生的时候执行下一步
                                channel.subscribe(effects.actionType, next);    //此时订阅
                                break;
                            case 'PUT':   //effects:{type:'PUT',action:{type:INCREMENT}
                                dispatch(effects.action);
                                next();
                                break;
                            case 'FORK':
                                run(effects.task);   //如果是fork的话就开启一个新的子进程去执行
                                next();  //自己的saga会立刻继续执行下去，而不会在此等待
                                break;
                            case 'CALL':
                                effects.fn(...effects.args).then(next);   //返回promise，把next当作一个成功的回调
                            case 'CPS':
                                effects.fn(...effects.args, next);
                            case 'ALL':
                                let times = function (cb, length) {
                                    let count = 0;
                                    return function () {
                                        if (++count === length) {
                                            cb();
                                        }
                                    }
                                };
                                let fns = effects.fns;
                                let done = times(next, fns.length);  //期待一个函数被调用多次后，才让真正的函数执行的话，可以采用这种写法
                                (effects.fns || []).forEach((fn) => run(fn, done));
                                break;
                            default :
                                break;
                        }

                    }

                } else {
                    callback && callback();
                }
            }

            next()

            return next
        }

        sagaMiddleware.run = run;

        return function (next) {
            return function (action) {
                //派发动作的时候发布
                channel.publish(action);
                next(action)
            }
        }
    }


    return sagaMiddleware;
}
